home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / splines / splines.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  34KB  |  1,177 lines

  1. /*
  2.  *    @(#) splines.c 1.8 93/11/02 MRINC
  3.  *
  4.  *    Written using CGI 5 October 1987 by Ron Record (sco!rr)
  5.  *  Rewritten using X11 20 Apr 1993 by Ron Record (rr@sco.com)
  6.  */
  7. /*************************************************************************
  8.  *                                                                       *
  9.  *  Copyright (c) 1987-1993 Ronald Joe Record                            *
  10.  *                                                                       *
  11.  *  All rights reserved. No part of this program or publication may be   *
  12.  *  reproduced, transmitted, transcribed, stored in a retrieval system,  *
  13.  *  or translated into any language or computer language, in any form or *
  14.  *  by any means, electronic, mechanical, magnetic, optical, chemical,   *
  15.  *  biological, or otherwise, without the prior written permission of:   *
  16.  *                                                                       *
  17.  *      Ronald Joe Record (408) 458-3718                                 *
  18.  *      212 Owen St., Santa Cruz, California 95062 USA                   *
  19.  *                                                                       *
  20.  *************************************************************************/
  21.  
  22.  
  23. #include "splines.h"
  24.  
  25. #define BMAX 32
  26. #define M_ESIZ 512
  27. #define M_X 12
  28. #define M_Y 8
  29. #define M_DELTA 0.05
  30. #define MAX_CINC 8
  31.  
  32. typedef struct {
  33.     int x,y;
  34.     int xdir, ydir;
  35.     int color;
  36. } BALL;
  37.  
  38. BALL balls[BMAX];
  39. int xradius=5, yradius=4, erase_balls=1, erase_curve=0, c_color, ch_wheel=1;
  40. int lines=0, curve=1, nsteps=0, showballs=0, nballs=4, nsplines=1, e_color;
  41. int naptime=0, stopping=1, now_c=0, delay_erase=1, esiz=64, s_order=3;
  42. int nummaps=1, delay=0, bezier=0, spinlines=0, solospin=0;
  43. int useroot=0, Fflag=0, oflag=0, ranwalk=0, ranflag=0, spin=0, xor=0;
  44. int width=0, height=0, maxcolor, numfreecols;
  45. int ***ctrl;
  46. long limit=0, num_c=1;
  47. double *theta;
  48. int *radius;
  49. char *outname;
  50.  
  51. /* routines declared in this file */
  52. void event_loop(), usage(), init_contexts(), drawball(), redraw(), Clear();
  53. void init_ctrl(), init_pts(), print_help(), drawspline(), ranparams();
  54. void redisplay(), resize(), save(), move(), parseargs(), Getkey(); 
  55. void freemem(), setupmem();
  56. /* external routines used in this file */
  57. extern int rand();
  58. extern void Draw_spline(), Draw_bez();
  59.  
  60. void
  61. usage()
  62. {
  63.     printf("Usage: splines [-abefrsuxBEFIRW][-w width][-h height][-o file]\n");
  64.     printf("\t[-m index][-c colorwheel][-n count][-N nballs][-l limit]\n");
  65.     printf("\t[-p nsteps][-D delay][-S naptime][-X xradius][-Y yradius]\n");
  66.     printf("\t-a indicates display control points only\n");
  67.     printf("\t-B indicates Bezier rather than B-spline\n");
  68.     printf("\t-b indicates display cubic curves only\n");
  69.     printf("\t-c selects color wheel\n");
  70.     printf("\t-D specifies the spin delay\n");
  71.     printf("\t-E indicates erasure of previous control points\n");
  72.     printf("\t-e indicates erasure of previous curve\n");
  73.     printf("\t-f indicates display control points as circles\n");
  74.     printf("\t-I indicates infinite mode\n");
  75.     printf("\t-l sets the limit of number of curves to be calculated\n");
  76.     printf("\t-N sets the number of control points\n");
  77.     printf("\t-n sets the limit of number of sequences of curves\n");
  78.     printf("\t-p sets the number of steps used in spline calculation\n");
  79.     printf("\t-R indicates use of the root window\n");
  80.     printf("\t-r indicates random selection of parameters\n");
  81.     printf("\t-S sets the delay between curves\n");
  82.     printf("\t-s indicates spin color wheel when done computing\n");
  83.     printf("\t-R indicates use the root window\n");
  84.     printf("\t-m # indicates a minimum color index of # (0-255)\n");
  85.     printf("\t-o file will save the output as 'file' in PPM format\n");
  86.     printf("\t-W specifies a pseudo-random walk of the control points\n");
  87.     printf("\t-w # indicates a window width of #\n");
  88.     printf("\t-X sets the X radius of control point disks\n");
  89.     printf("\t-x indicates use of XOR rather than COPY mode\n");
  90.     printf("\t-Y sets the Y radius of control point disks\n");
  91.     printf("\t-h # indicates a window height of #\n");
  92.     printf("\t-u produces this message\n");
  93.     printf("During display :\n");
  94.     printf("\t'f' or 'F' will save the picture as a PPM file\n");
  95.     printf("\t'r' or 's' will spin the color wheel forwards or backwards\n");
  96.     printf("\t'W' will increment and 'w' decrement the color map selection\n");
  97.     printf("\t'?' or 'h' will display the usage message\n");
  98.     printf("\t'q' or 'Q' will quit\n");
  99. }
  100.  
  101. void
  102. freemem()
  103. {
  104.     static int i, j;
  105.  
  106.     for (i=0;i<M_ESIZ;i++) {
  107.         for (j=0;j<nballs;j++)
  108.             free(ctrl[i][j]);
  109.         free(ctrl[i]);
  110.     }
  111.     free(ctrl);
  112.     free(theta);
  113.     free(radius);
  114. }
  115.  
  116. void
  117. setupmem()
  118. {
  119.     static int i, j;
  120.  
  121.     if ((ctrl = (int ***)malloc((unsigned)((M_ESIZ+1)*sizeof(int **)))) == NULL) {
  122.         fprintf(stderr, "Error malloc'ing ctrl\n");
  123.         exit(-1);
  124.     }
  125.     if ((theta = (double *)malloc((unsigned)M_ESIZ*sizeof(double))) == NULL) {
  126.         fprintf(stderr, "Error malloc'ing theta\n");
  127.         exit(-1);
  128.     }
  129.     if ((radius = (int *)malloc((unsigned)M_ESIZ*sizeof(int))) == NULL) {
  130.         fprintf(stderr, "Error malloc'ing radius\n");
  131.         exit(-1);
  132.     }
  133.     for (i=0;i<M_ESIZ;i++) {
  134.         if ((ctrl[i]=(int **)malloc((unsigned)((nballs+1)*sizeof(int *))))==NULL){
  135.             fprintf(stderr, "Error malloc'ing ctrl[%d]\n",i);
  136.             exit(-1);
  137.         }
  138.         for (j=0;j<nballs;j++) {
  139.             if ((ctrl[i][j] = (int *)malloc((unsigned)(2*sizeof(int))))==NULL) {
  140.                 fprintf(stderr, "Error malloc'ing ctrl[%d][%d]\n",i,j);
  141.                 exit(-1);
  142.             }
  143.         }
  144.     }
  145. }
  146.  
  147. void
  148. drawspline(col, n)
  149. int col, n;
  150. {
  151.     static int i, j, k, m;
  152.     XSegment points[BMAX];
  153.     extern XSegment clipline();
  154.     extern double sin(), cos();
  155.  
  156.     if (lines) {
  157.         for (i=0; i<(nballs/2); i++) {
  158.             j = 2*i;
  159.             points[i].x1 = ctrl[n][j][0];
  160.             points[i].y1 = ctrl[n][j][1];
  161.             points[i].x2 = ctrl[n][j+1][0];
  162.             points[i].y2 = ctrl[n][j+1][1];
  163.         }
  164.         XDrawSegments(dpy,pixmap,Data_GC[xor][col],points,nballs/2);
  165.         XDrawSegments(dpy,canvas,Data_GC[xor][col],points,nballs/2);
  166.     }
  167.     else if (spinlines) {
  168.         m = solospin ? 1 : nballs;
  169.         for (i=0; i<m; i++) {
  170.             j = radius[n] * sin(theta[n]);
  171.             k = radius[n] * cos(theta[n]);
  172.             points[i].x1 = j + ctrl[n][i][0];
  173.             points[i].y1 = k + ctrl[n][i][1];
  174.             points[i].x2 = ctrl[n][i][0] - j;
  175.             points[i].y2 = ctrl[n][i][1] - k;
  176.             points[i] = clipline(points[i],0,width,0,height);
  177.         }
  178.         XDrawSegments(dpy,pixmap,Data_GC[xor][col],points,m);
  179.         XDrawSegments(dpy,canvas,Data_GC[xor][col],points,m);
  180.         theta[(n+1)%esiz] = theta[n] + M_DELTA;
  181.         if (theta[(n+1)%esiz] > M_2PI)
  182.             theta[(n+1)%esiz] = theta[(n+1)%esiz] - M_2PI;
  183.         if (n < (esiz/2))
  184.             radius[(n+1)%esiz] = Max(radius[n] + 1, 1);
  185.         else
  186.             radius[(n+1)%esiz] = Max(radius[n] - 1, 1);
  187.     }
  188.     else {
  189.         j = (nballs / nsplines);
  190.         for (i=0; i<nsplines; i++) {
  191.             if (bezier)
  192.                 Draw_bez(dpy,canvas,pixmap,Data_GC[xor][col],
  193.                             &ctrl[n][i*j],j-1,nsteps);
  194.             else
  195.                 Draw_spline(dpy,canvas,pixmap,Data_GC[xor][col],
  196.                             &ctrl[n][i*j],j-1,nsteps,s_order);
  197.         }
  198.     }
  199. }
  200.  
  201. void
  202. ranparams()
  203. {
  204.     delay_erase = rand() % 2;
  205.     curve = rand() % 2;
  206.     showballs = rand() % 2;
  207.     if ((!curve) && (!showballs))
  208.         if (rand() % 2)
  209.             curve = 1;
  210.         else
  211.             showballs = 1;
  212.     if (delay_erase && curve) {
  213.         showballs = xor = 0;
  214.     }
  215.     else {
  216.         xor = rand() % 2;
  217.     }
  218.     erase_curve = rand() % 2;
  219.     erase_balls = rand() % 2;
  220.     spin = rand() % 2;
  221.     Fflag = rand() % 2;
  222.     bezier = rand() % 2;
  223.     lines = (!(rand() % 6));
  224.     spinlines = (!(rand() % 6));
  225.     nballs = rand() % BMAX;
  226.     if (nballs < 3)
  227.         nballs = 3;
  228.     nsplines = (rand() % (nballs/3)) + 1;
  229.     if (nsplines < 1)
  230.         nsplines = 1;
  231.     if (nsplines > (nballs/3))
  232.         nsplines = nballs/3;
  233.     /* work-around bug whereby if nballs>13 per Bezier spline, */
  234.     /* the spline is always anchored at the origin */
  235.     if (bezier && ((nballs/nsplines) > 13))
  236.         bezier = 0;
  237.     nsteps = ((nballs/nsplines) * 2) + (rand() % 50);
  238.     if (rand() % 4)
  239.         naptime = 0;
  240.     else
  241.         naptime = rand() % (450 / nballs);
  242.     numwheels = rand() % (MAXWHEELS + 1);
  243. }
  244.  
  245. void
  246. init_contexts()
  247. {
  248.     static int i, j;
  249.     XGCValues values;
  250.  
  251.     /*
  252.      * create default, writable, graphics contexts for the canvas.
  253.      * the first index indicates whether the context draws in XOR mode.
  254.      * the second index indicates what the foreground color is.
  255.      */
  256.     values.background = BlackPixel(dpy, screen);
  257.     for (j=0; j<2; j++) {
  258.         if (j)
  259.             values.function = GXxor;
  260.         else
  261.             values.function = GXcopy;
  262.         Data_GC[j][0] = XCreateGC(dpy, DefaultRootWindow(dpy),
  263.             (unsigned long) NULL, (XGCValues *) NULL);
  264.         /* set the background to black */
  265.         XSetBackground(dpy,Data_GC[j][0],BlackPixel(dpy, screen));
  266.         /* set the foreground of the 0th context to black */
  267.         XSetForeground(dpy, Data_GC[j][0], BlackPixel(dpy, screen));
  268.         Data_GC[j][1] = XCreateGC(dpy, DefaultRootWindow(dpy),
  269.             (unsigned long) NULL, (XGCValues *) NULL);
  270.         /* set the background to black */
  271.         XSetBackground(dpy,Data_GC[j][1],BlackPixel(dpy, screen));
  272.         /* set the foreground of the 1st context to white */
  273.         XSetForeground(dpy, Data_GC[j][1], WhitePixel(dpy,  screen));
  274.         for (i=2; i<maxcolor; i++) {
  275.             values.foreground = i;
  276.             Data_GC[j][i] = XCreateGC(dpy, DefaultRootWindow(dpy),
  277.                             GCForeground | GCBackground | GCFunction, &values);
  278.         }
  279.     }
  280. }
  281.     
  282. void
  283. Clear()
  284. {
  285.     XFillRectangle(dpy, pixmap, Data_GC[0][0], 0, 0, width, height);
  286.     XCopyArea(dpy, pixmap, canvas, Data_GC[0][0], 0, 0, width, height, 0, 0);
  287. }
  288.  
  289. void 
  290. init_ball(k)
  291. int k;
  292. {
  293.     balls[k].x = (int)((rand()%(width-2))+1);
  294.     balls[k].y = (int)((rand()%(height-2))+1);
  295.     balls[k].xdir = (rand()&0xff) > 128 ? rand()%M_X+1 :-(rand()%M_X+1);
  296.     balls[k].ydir = (rand()&0xff) > 128 ? rand()%M_Y+1 :-(rand()%M_Y+1);
  297.     balls[k].color = (rand() % numfreecols) + STARTCOLOR;
  298. }
  299.  
  300. void
  301. init_ctrl()
  302. {
  303.     int i;
  304.  
  305.     for (i=0;i<nballs;i++) {
  306.         ctrl[now_c][i][0] = balls[i].x;
  307.         ctrl[now_c][i][1] = balls[i].y;
  308.     }
  309. }
  310.  
  311. void
  312. init_pts()
  313. {
  314.     int i;
  315.  
  316.     for (i=0;i<nballs;i++) {
  317.         init_ball(i);
  318.         if (showballs)
  319.             drawball(i, balls[i].color);
  320.     }
  321. }
  322.  
  323. initialize()
  324. {
  325.     Clear();
  326.     num_c = 1;
  327.     now_c = 0;
  328.     theta[0] = 0.0;
  329.     radius[0] = 1;
  330.     init_pts();
  331.     init_ctrl();
  332.     c_color = STARTCOLOR;
  333.     if (xor)
  334.         e_color = c_color;
  335.     else
  336.         e_color = 0;
  337.     if (curve)    /* Draw the initial spline curve */
  338.         drawspline(c_color, now_c);
  339. }
  340.  
  341. #define x_str 10
  342.  
  343. void
  344. print_help() 
  345. {
  346.     static char str[80];
  347.     static int y_str, spacing;
  348.     static int ascent, descent, dir;
  349.     static XCharStruct overall;
  350.     static GC gc;
  351.  
  352.     gc = Data_GC[0][1];
  353.     XClearWindow(dpy, help);
  354.     y_str = 20;
  355.     sprintf(str,"During run-time, interactive control can be exerted via : ");
  356.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  357.     XQueryTextExtents(dpy,(XID)XGContextFromGC(gc),"Hey!",
  358.             4,&dir,&ascent,&descent,&overall);
  359.     spacing = ascent + descent + 5;
  360.     y_str += spacing;
  361.     sprintf(str,"        < decreases delay between curves, > increases it");
  362.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  363.     y_str += spacing;
  364.     sprintf(str,"        - lowers the value of mincolindex, + raises it");
  365.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  366.     y_str += spacing;
  367.     sprintf(str,"        b or B toggles between Bezier and B-spline");
  368.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  369.     y_str += spacing;
  370.     sprintf(str,"        c or C clears the window or creates a new control pt");
  371.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  372.     y_str += spacing;
  373.     sprintf(str,"        d or D decreases or increases the spin delay");
  374.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  375.     y_str += spacing;
  376.     sprintf(str,"        e or E toggles erasing of curves or control pts");
  377.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  378.     y_str += spacing;
  379.     sprintf(str,"        f or F saves splines to a PPM file");
  380.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  381.     y_str += spacing;
  382.     sprintf(str,"        g or G toggles display of ctrl pts as disks");
  383.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  384.     y_str += spacing;
  385.     sprintf(str,"        h or H or ? displays this message");
  386.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  387.     y_str += spacing;
  388.     sprintf(str,"        i or I toggles infinite mode");
  389.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  390.     y_str += spacing;
  391.     sprintf(str,"        k or K toggles display of control pts or curves");
  392.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  393.     y_str += spacing;
  394.     sprintf(str,"        L deletes a control point");
  395.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  396.     y_str += spacing;
  397.     sprintf(str,"        n goes on to the next splines");
  398.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  399.     y_str += spacing;
  400.     sprintf(str,"        N creates a new replacement splines");
  401.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  402.     y_str += spacing;
  403.     sprintf(str,"        p or P decreases or increases the number of steps");
  404.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  405.     y_str += spacing;
  406.     sprintf(str,"        R toggles a random walk of the control points");
  407.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  408.     y_str += spacing;
  409.     sprintf(str,"        S stops the spinning of the color wheel");
  410.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  411.     y_str += spacing;
  412.     sprintf(str,"        r or s spins the colorwheel");
  413.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  414.     y_str += spacing;
  415.     sprintf(str,"        w decrements, W increments the color wheel index");
  416.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  417.     y_str += spacing;
  418.     sprintf(str,"        X creates an additional control point");
  419.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  420.     y_str += spacing;
  421.     sprintf(str,"        x toggles between XOR and COPY mode");
  422.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  423.     y_str += spacing;
  424.     sprintf(str,"        q or Q exits");
  425.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  426.     y_str += spacing;
  427.     sprintf(str,"Press 'h', 'H', or '?' to unmap the help window");
  428.     XDrawImageString(dpy,help,gc,x_str,y_str,str,strlen(str));
  429. }
  430.  
  431. void
  432. redisplay (event)
  433. XExposeEvent    *event;
  434. {
  435.     if ((event->window == help) && (!useroot))
  436.         print_help();
  437.     else {
  438.         /*
  439.         * Extract the exposed area from the event and copy
  440.         * from the saved pixmap to the window.
  441.         */
  442.         XCopyArea(dpy, pixmap, canvas, Data_GC[0][0], event->x, event->y, 
  443.             event->width, event->height, event->x, event->y);
  444.     }
  445. }
  446.  
  447. void
  448. resize()
  449. {
  450.     Window r;
  451.     int j; 
  452.     int x, y;
  453.     unsigned int bw, d, new_w, new_h;
  454.  
  455.     XGetGeometry(dpy,canvas,&r,&x,&y,&new_w,&new_h,&bw,&d);
  456.     if (((int)new_w == width) && ((int)new_h == height))
  457.         return;
  458.     width = (int)new_w; height = (int)new_h;
  459.     if (pixmap)
  460.         XFreePixmap(dpy, pixmap);
  461.     pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), 
  462.             width, height, DefaultDepth(dpy, screen));
  463.     initialize();
  464. }
  465.  
  466. void
  467. Cleanup() {
  468.     XCloseDisplay(dpy);
  469. }
  470.  
  471. /* Store splines growth in PPM format */
  472. void
  473. save()
  474. {
  475.     FILE *outfile;
  476.     unsigned char c;
  477.     XImage *ximage;
  478.     static int i,j;
  479.     struct Colormap {
  480.         unsigned char red;
  481.         unsigned char green;
  482.         unsigned char blue;
  483.     };
  484.     struct Colormap *colormap=NULL;
  485.  
  486.     if ((colormap=
  487.         (struct Colormap *)malloc((unsigned)sizeof(struct Colormap)*maxcolor))
  488.             == NULL) {
  489.         fprintf(stderr,"Error malloc'ing colormap array\n");
  490.         Cleanup();
  491.         exit(-1);
  492.     }
  493.     outfile = fopen(outname,"w");
  494.     if(!outfile) {
  495.         perror(outname);
  496.         Cleanup();
  497.         exit(-1);
  498.     }
  499.  
  500.     ximage=XGetImage(dpy, pixmap, 0, 0, width, height, AllPlanes, ZPixmap);
  501.  
  502.     for (i=0;i<maxcolor;i++) {
  503.         colormap[i].red=(unsigned char)(Colors[i].red >> 8);
  504.         colormap[i].green=(unsigned char)(Colors[i].green >> 8);
  505.         colormap[i].blue =(unsigned char)(Colors[i].blue >> 8);
  506.     }
  507.     fprintf(outfile,"P%d %d %d\n",6,width,height);
  508.     fprintf(outfile,"%d\n",maxcolor-1);
  509.  
  510.     for (j=0;j<height;j++)
  511.         for (i=0;i<width;i++) {
  512.             c = (unsigned char)XGetPixel(ximage,i,j);
  513.             fwrite((char *)&colormap[c],sizeof colormap[0],1,outfile);
  514.         }
  515.     fclose(outfile);
  516.     free(colormap);
  517. }
  518.  
  519. void
  520. redraw()
  521. {
  522.     static int i;
  523.  
  524.     Clear();
  525.     if (curve) /* Redraw the erased spline curve */
  526.         drawspline(c_color, now_c);
  527.     if (showballs) /* Redraw the erased balls */
  528.         for (i=0; i<nballs; i++)
  529.             drawball(i, balls[i].color);
  530. }
  531.  
  532. void
  533. Getkey(event)
  534. XKeyEvent *event;
  535. {
  536.     char key;
  537.     static int spinning=0, spindir=0, i;
  538.     static XWindowAttributes attr;
  539.     extern void init_color(), write_cmap();
  540.  
  541.     if (XLookupString(event, (char *)&key, sizeof(key), (KeySym *)0,
  542.        (XComposeStatus *) 0) > 0)
  543.             switch (key) {
  544.                 case '\015': /*write out current colormap to $HOME/.<prog>map*/
  545.                     write_cmap(dpy,cmap,Colors,maxcolor,"splines","Splines");
  546.                     break;
  547.                 case '<': naptime -= 20;
  548.                     if (naptime < 0)
  549.                         naptime = 0;
  550.                     break;
  551.                 case '>': naptime += 20;
  552.                     break;
  553.                 case '-': /* decrease nsteps */
  554.                     nsteps -= 10;
  555.                     if (nsteps < 1)
  556.                         nsteps = 1;
  557.                     redraw();
  558.                     break;
  559.                 case '+': /* Increase nsteps */
  560.                     nsteps += 10;
  561.                     redraw();
  562.                     break;
  563.                 case ']': mincolindex += INDEXINC;
  564.                     if (mincolindex > maxcolor)
  565.                         mincolindex = 1;
  566.                     init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  567.                         mincolindex,maxcolor,numwheels,"splines","Splines",0);
  568.                     break;
  569.                 case '[': mincolindex -= INDEXINC;
  570.                     if (mincolindex < 1)
  571.                         mincolindex = maxcolor - 1;
  572.                     init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  573.                         mincolindex,maxcolor,numwheels,"splines","Splines",0);
  574.                     break;
  575.                 case 'a':
  576.                 case 'A': ch_wheel = (!ch_wheel);
  577.                     break;
  578.                 case 'b':
  579.                 case 'B': bezier = (!bezier);
  580.                 /* work-around bug whereby if nballs>13 per Bezier spline, */
  581.                 /* the spline is always anchored at the origin */
  582.                     if (bezier && ((nballs/nsplines) > 13))
  583.                         nballs = nsplines * 13;
  584.                     redraw();
  585.                     break;
  586.                 case 'c':    /* clear the window */
  587.                     redraw();
  588.                     break;
  589.                 case 'C':    /* create a new ship, leaving the old */
  590.                     if (nballs >= BMAX - 1)
  591.                         break;
  592.                     freemem();
  593.                     nballs++;
  594.             /* work-around bug whereby if nballs>13 per Bezier spline, */
  595.             /* the spline is always anchored at the origin */
  596.                     if (bezier && ((nballs/nsplines) > 13))
  597.                         nballs = nsplines * 13;
  598.                     setupmem();
  599.                     if (spinlines)
  600.                         initialize();
  601.                     else {
  602.                         init_ball(nballs-1);/* initialize new ship */
  603.                         init_ctrl();
  604.                         redraw();
  605.                     }
  606.                     break;
  607.                 case 'd': delay -= 25; if (delay < 0) delay = 0; break;
  608.                 case 'D': delay += 25; break;
  609.                 case 'e': erase_curve = (!erase_curve);
  610.                     if (erase_curve)
  611.                         redraw();
  612.                     break;
  613.                 case 'E': erase_balls = (!erase_balls);
  614.                     if (erase_balls)
  615.                         redraw();
  616.                     break;
  617.                 case 'f':    /* save in PPM format file */
  618.                 case 'F': save(); break;
  619.                 case 'g': Fflag = (!Fflag);
  620.                     redraw();
  621.                     break;
  622.                 case 'G':    /* toggle drawing solo spun lines */
  623.                     solospin = (!solospin);
  624.                     redraw();
  625.                     break;
  626.                 case 'i':
  627.                 case 'I': stopping = (!stopping);
  628.                     break;
  629.                 case 'k':    /* toggle display of balls */
  630.                     showballs = (!showballs);
  631.                     redraw();
  632.                     break;
  633.                 case 'K':    /* toggle display of curves */
  634.                     curve = (!curve);
  635.                     redraw();
  636.                     break;
  637.                 case 'L':    /* toggle drawing lines */
  638.                     lines = (!lines);
  639.                     redraw();
  640.                     break;
  641.                 case '\014': /* (ctrl-L) toggle drawing spinning lines */
  642.                     spinlines = (!spinlines);
  643.                     initialize();
  644.                     break;
  645.                 case 'l':    /* delete a ball */
  646.                     if (nballs <= 3)
  647.                         break;
  648.                     freemem();
  649.                     nballs--;
  650.                     setupmem();
  651.                     redraw();
  652.                     break;
  653.                 case 'm': esiz /= 2; /* halve the length of the trail */
  654.                     if (esiz < 1)
  655.                         esiz = 1;
  656.                     initialize();
  657.                     break;
  658.                 case 'M': esiz *= 2; /* double the length of the trail */
  659.                     if (esiz > M_ESIZ)
  660.                         esiz = M_ESIZ;
  661.                     initialize();
  662.                     break;
  663.                 case 'N':    /* go on to the next splines */
  664.                     nummaps++;    /* but don't increment the splines counter */
  665.                 case 'n':    /* go on to the next splines */
  666.                     if (ranflag) {
  667.                         freemem();
  668.                         ranparams();
  669.                         setupmem();
  670.                     }
  671.                     initialize();
  672.                     break;
  673.                 case 'o': /* decrease order of spline */
  674.                     if (--s_order < 1)
  675.                         s_order = 1;
  676.                     redraw();
  677.                     break;
  678.                 case 'O': /* Increase order of spline */
  679.                     if (++s_order > 4)
  680.                         s_order = 4;
  681.                     redraw();
  682.                     break;
  683.                 case 'p': /* decrease nsplines */
  684.                     if (--nsplines < 1)
  685.                         nsplines = 1;
  686.                     redraw();
  687.                     break;
  688.                 case 'P': /* Increase nsplines */
  689.                     if (++nsplines > nballs/3)
  690.                         nsplines = nballs/3;
  691.                     redraw();
  692.                     break;
  693.                 case 'R': ranwalk = (!ranwalk);
  694.                     break;
  695.                 case 'r': spinning=1; spindir=1; 
  696.                     Spin(dpy, cmap, Colors, STARTCOLOR, maxcolor, delay, 1);
  697.                     break;
  698.                 case 'S': spinning=0;
  699.                     break;
  700.                 case 's': spinning=1; spindir=0;
  701.                     Spin(dpy, cmap, Colors, STARTCOLOR, maxcolor, delay, 0);
  702.                     break;
  703.                 case 'T': delay_erase = (!delay_erase);
  704.                     if (delay_erase) {
  705.                         showballs = 0; xor = 0;
  706.                         e_color = 0;
  707.                     }
  708.                     now_c=0;
  709.                     redraw();
  710.                     break;
  711.                 case '\027': /* (ctrl-W) read palette from $HOME/.splinesmap */
  712.                   numwheels = 0;
  713.                   init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  714.                         mincolindex,maxcolor,numwheels,"splines","Splines",0);
  715.                   break;
  716.                 case 'W': 
  717.                     if (numwheels < MAXWHEELS)
  718.                         numwheels++;
  719.                     else
  720.                         numwheels = 0;
  721.                     init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  722.                         mincolindex,maxcolor,numwheels,"splines","Splines",0);
  723.                     break;
  724.                 case 'w': 
  725.                     if (numwheels > 0)
  726.                         numwheels--;
  727.                     else
  728.                         numwheels = MAXWHEELS;
  729.                     init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  730.                         mincolindex,maxcolor,numwheels,"splines","Splines",0);
  731.                     break;
  732.                 case '?':
  733.                 case 'h': 
  734.                     if (!useroot) {
  735.                         XGetWindowAttributes(dpy, help, &attr);
  736.                         if (attr.map_state != IsUnmapped)
  737.                             XUnmapWindow(dpy, help);
  738.                         else {
  739.                             XMapRaised(dpy, help);
  740.                             print_help();
  741.                         }
  742.                     }
  743.                     break;
  744.                 case 'x':    /* toggle drawing in XOR mode */
  745.                     xor = (!xor);
  746.                     if (xor)
  747.                         e_color = c_color;
  748.                     else
  749.                         e_color = 0;
  750.                     redraw();
  751.                     break;
  752.                 case 'X':    /* create new splines, erasing the old */
  753.                         nummaps++;
  754.                         initialize();
  755.                         break;
  756.                 case 'Q':
  757.                 case 'q': Cleanup(); exit(0); break;
  758.             }
  759.             if (spinning)
  760.                 Spin(dpy, cmap, Colors, STARTCOLOR, maxcolor, delay, spindir);
  761. }
  762.  
  763. void
  764. parseargs(argc, argv)
  765. int argc;
  766. char *argv[];
  767. {
  768.    int c;
  769.    extern int optind, getopt();
  770.    extern char *optarg;
  771.  
  772.      outname = "splines.ppm";
  773.      while((c=getopt(argc,argv,
  774.         "abefrsuxBCEFGILRTWc:h:k:l:m:n:o:p:w:D:M:N:O:P:S:"))!=EOF)
  775.     {    switch(c)
  776.         {
  777.         case 'a':    curve=0; showballs=1; break; /* balls only */
  778.         case 'b':    curve=1; showballs=1; break; /* curve and balls */
  779.         case 'c':
  780.             numwheels = atoi(optarg);
  781.             if (numwheels > MAXWHEELS)
  782.                 numwheels = MAXWHEELS;
  783.             if (numwheels < 0)
  784.                 numwheels = 0;
  785.             break;
  786.         case 'e':
  787.             erase_curve++;
  788.             break;
  789.         case 'f':
  790.             Fflag=1;
  791.             break;
  792.         case 'u':
  793.             usage();
  794.             exit(0);
  795.         case 'h':
  796.             height = atoi(optarg);
  797.             break;
  798.         case 'l':
  799.             limit = atol(optarg);
  800.             break;
  801.         case 'm':
  802.             mincolindex = atoi(optarg);
  803.             break;
  804.         case 'n':
  805.             nummaps = atoi(optarg);
  806.             break;
  807.         case 'o':
  808.             ++oflag;
  809.             outname = optarg;
  810.             break;
  811.         case 'p':
  812.             nsteps = atoi(optarg);
  813.             break;
  814.         case 'r':
  815.             ++ranflag;
  816.             break;
  817.         case 's':
  818.             ++spin;
  819.             break;
  820.         case 'w':
  821.             width = atoi(optarg);
  822.             break;
  823.         case 'x':    /* use xor as drawing mode */
  824.             xor = 1;
  825.             break;
  826.         case 'B':
  827.             bezier++;
  828.             break;
  829.         case 'C':    /* don't change color wheels automatically */
  830.             ch_wheel = 0;
  831.             break;
  832.         case 'D':
  833.             delay = atoi(optarg);
  834.             break;
  835.         case 'E':
  836.             erase_balls++;
  837.             break;
  838.         case 'F':
  839.             spinlines++;
  840.             break;
  841.         case 'G':
  842.             solospin++;
  843.             break;
  844.         case 'I':
  845.             stopping = 0;
  846.             break;
  847.         case 'L':
  848.             lines = 1;
  849.             break;
  850.         case 'M':
  851.             esiz = atoi(optarg);
  852.             if (esiz > M_ESIZ)
  853.                 esiz = M_ESIZ;
  854.             if (esiz < 1)
  855.                 esiz = 1;
  856.             break;
  857.         case 'N':
  858.             nballs = atoi(optarg);
  859.             if (nballs > BMAX)
  860.                 nballs = BMAX;
  861.             if (nballs < 3)
  862.                 nballs = 3;
  863.             break;
  864.         case 'O':
  865.             s_order = atoi(optarg);
  866.             if (s_order < 1)
  867.                 s_order = 1;
  868.             if (s_order > 4)
  869.                 s_order = 4;
  870.             break;
  871.         case 'P':
  872.             nsplines = atoi(optarg);
  873.             if (nsplines > BMAX)
  874.                 nsplines = BMAX;
  875.             if (nsplines < 1)
  876.                 nsplines = 1;
  877.             break;
  878.         case 'R':
  879.             useroot++;
  880.             ch_wheel = 0;
  881.             break;
  882.         case 'S':
  883.             naptime = atoi(optarg);
  884.             break;
  885.         case 'T':
  886.             delay_erase=0;
  887.             break;
  888.         case 'W':    ranwalk++; break;
  889.         case 'X':    xradius = atoi(optarg); break;
  890.         case 'Y':    yradius = atoi(optarg); break;
  891.         case '?':
  892.             usage();
  893.             exit(1);
  894.             break;
  895.         }
  896.     }
  897.     if (ranflag)
  898.         ranparams();
  899.     if (nsplines > nballs/3)
  900.         nsplines = nballs/3;
  901.     /* work-around bug whereby if nballs>13 per Bezier spline, */
  902.     /* the spline is always anchored at the origin */
  903.     if (bezier && ((nballs/nsplines) > 13))
  904.         nballs = nsplines * 13;
  905. }
  906.  
  907. void
  908. event_loop()
  909. {
  910.     int n;
  911.     XEvent event;
  912.  
  913.     n = XEventsQueued(dpy, QueuedAfterFlush);
  914.     while (n-- > 0) {
  915.         XNextEvent(dpy, &event);
  916.         switch(event.type) {
  917.             case KeyPress:
  918.                 Getkey(&event);
  919.                 break;
  920.             case Expose:
  921.                 if (useroot)
  922.                     redraw();
  923.                 else
  924.                     redisplay(&event);
  925.                 break;
  926.             case ConfigureNotify:
  927.                 resize();
  928.                 break;
  929.         }
  930.     }
  931. }
  932.  
  933. void
  934. drawball(i, col)
  935. int i, col;
  936. {
  937.     if (Fflag) {
  938.           XFillArc(dpy,pixmap,Data_GC[1][col],
  939.                 balls[i].x-xradius,balls[i].y-yradius,
  940.                 2*xradius,2*yradius,0,23040);
  941.           XFillArc(dpy,canvas,Data_GC[1][col],
  942.                 balls[i].x-xradius,balls[i].y-yradius,
  943.                 2*xradius,2*yradius,0,23040);
  944.     }
  945.     else {
  946.           XDrawArc(dpy,pixmap,Data_GC[1][col],
  947.                 balls[i].x-xradius,balls[i].y-yradius,
  948.                 2*xradius,2*yradius,0,23040);
  949.           XDrawArc(dpy,canvas,Data_GC[1][col],
  950.                 balls[i].x-xradius,balls[i].y-yradius,
  951.                 2*xradius,2*yradius,0,23040);
  952.     }
  953. }
  954.  
  955. void
  956. erase_old()
  957. {
  958.     static int i;
  959.  
  960.     if (spinlines)
  961.         Clear();
  962.     else
  963.         for (i=0; i<esiz; i++)
  964.             drawspline(0, i);
  965.     now_c = 0;
  966. }
  967.  
  968. void
  969. move() 
  970. {
  971.     static int i;
  972.  
  973.     ++num_c;
  974.     if (curve && erase_curve)    /* Erase the drawn spline curve */
  975.         drawspline(e_color, now_c);
  976.     if (curve && delay_erase) {
  977.         if (num_c >= esiz) /* erase esiz ago curve */
  978.             drawspline(0, num_c % esiz);
  979.         if (++now_c >= esiz)
  980.             now_c = 0;
  981.     }
  982.     for( i=0; i<nballs; i++ )
  983.     {
  984.         if (showballs && erase_balls)
  985.             drawball(i, (erase_curve || delay_erase) ? 0 : balls[i].color);
  986.         if (ranwalk && ((rand()&0xff) > 192)) {
  987.             balls[i].xdir = (rand()&0xff) > 128 ? rand()%M_X+1 :-(rand()%M_X+1);
  988.             balls[i].ydir = (rand()&0xff) > 128 ? rand()%M_Y+1 :-(rand()%M_Y+1);
  989.         }
  990.         balls[i].x +=balls[i].xdir;
  991.         balls[i].y +=balls[i].ydir;
  992.         if( (balls[i].x <0) || (balls[i].x>width-1))
  993.         {
  994.             /* bounced off screen */
  995.             balls[i].xdir = -balls[i].xdir;
  996.             balls[i].x +=balls[i].xdir;
  997.             balls[i].x +=balls[i].xdir;
  998.         }
  999.         if( (balls[i].y <0) || (balls[i].y >height-1))
  1000.         {
  1001.             /* bounced off screen */
  1002.             balls[i].ydir = -balls[i].ydir;
  1003.             balls[i].y +=balls[i].ydir;
  1004.             balls[i].y +=balls[i].ydir;
  1005.         }
  1006.         if( showballs )
  1007.             drawball(i, balls[i].color);
  1008.         if (curve) {
  1009.             ctrl[now_c][i][0] = balls[i].x;
  1010.             ctrl[now_c][i][1] = balls[i].y;
  1011.         }
  1012.     }
  1013.     Timer(naptime);
  1014.     if (curve) { /* Draw the new spline curve */
  1015.         if (++c_color >= maxcolor) { /* increment the color index */
  1016.             if (ch_wheel) {/*cannot be using the root window if ch_wheel set*/
  1017.               if (ch_wheel++ == MAX_CINC) {
  1018.                 ch_wheel = 1;
  1019.                 if (curve && delay_erase)
  1020.                     erase_old();
  1021.                 if (numwheels > 0)
  1022.                     numwheels--;
  1023.                 else
  1024.                     numwheels = MAXWHEELS;
  1025.                 init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  1026.                       mincolindex,maxcolor,numwheels,"splines","Splines",0);
  1027.               }
  1028.             }
  1029.             c_color = STARTCOLOR;
  1030.         }
  1031.         if (xor)
  1032.             e_color = c_color;
  1033.         drawspline(c_color, now_c);
  1034.     }
  1035. }
  1036.  
  1037. main(argc,argv)
  1038. int argc;
  1039. char *argv[];
  1040. {
  1041.     static int i, j;
  1042.     static int count;
  1043.     XSizeHints hint;
  1044.     Atom __SWM_VROOT = None;
  1045.     Window rootReturn, parentReturn, *children;
  1046.     unsigned int numChildren;
  1047.     extern void srand(), init_color();
  1048.     
  1049.     srand((unsigned)time(0));
  1050.     parseargs(argc,argv);
  1051.     dpy = XOpenDisplay("");
  1052.     screen = DefaultScreen(dpy);
  1053.     if (useroot) {
  1054.         width = XDisplayWidth(dpy, screen);
  1055.         height = XDisplayHeight(dpy, screen);
  1056.     }
  1057.     if (width == 0)
  1058.         width = XDisplayWidth(dpy, screen);
  1059.     if (height == 0)
  1060.         height = XDisplayHeight(dpy, screen);
  1061.     if (nsteps == 0)
  1062.         nsteps = 5 * (nballs/nsplines);
  1063.     maxcolor  = (int)XDisplayCells(dpy, screen);
  1064.     if (maxcolor <= 16) {
  1065.         STARTCOLOR = 2; delay = 100;
  1066.         INDEXINC = 1; mincolindex = 5;
  1067.     }
  1068.     maxcolor = Min(maxcolor, MAXCOLOR);
  1069.     numfreecols = maxcolor - STARTCOLOR;
  1070.     if (limit == 0)
  1071.         limit = width * height;
  1072.     /*
  1073.     * Create the pixmap to hold the splines growth
  1074.     */
  1075.     pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, 
  1076.                            DefaultDepth(dpy, screen));
  1077.     /*
  1078.     * Create the window to display the fractal topographic map
  1079.     */
  1080.     hint.x = 0;
  1081.     hint.y = 0;
  1082.     hint.width = width;
  1083.     hint.height = height;
  1084.     hint.flags = PPosition | PSize;
  1085.     if (useroot) {
  1086.         canvas = DefaultRootWindow(dpy);
  1087.         /* search for virtual root (from ssetroot by Tom LaStrange) */
  1088.         __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
  1089.         XQueryTree(dpy,canvas,&rootReturn,&parentReturn,&children,&numChildren);
  1090.         for (j = 0; j < numChildren; j++) {
  1091.             Atom actual_type;
  1092.             int actual_format;
  1093.             long nitems, bytesafter;
  1094.             Window *newRoot = NULL;
  1095.  
  1096.             if (XGetWindowProperty (dpy, children[j], __SWM_VROOT,0,1, False, 
  1097.                 XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter,
  1098.                 (unsigned char **) &newRoot) == Success && newRoot) {
  1099.                 canvas = *newRoot;
  1100.                 break;
  1101.             }
  1102.         }
  1103.     }
  1104.     else {
  1105.         canvas = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
  1106.             0, 0, width, height, 5, 0, 1);
  1107.         XSetStandardProperties(dpy, canvas, "Splines by Ron Record",
  1108.             "Splines", None, argv, argc, &hint);
  1109.         XMapRaised(dpy, canvas);
  1110.     }
  1111.     XChangeProperty(dpy, canvas, XA_WM_CLASS, XA_STRING, 8, PropModeReplace, 
  1112.                     "splines", strlen("splines"));
  1113.     /*
  1114.     * Create the window used to display the help info (if not running on root)
  1115.     */
  1116.     if (!useroot) {
  1117.         hint.x = XDisplayWidth(dpy, screen) / 4;
  1118.         hint.y = XDisplayHeight(dpy, screen) / 5;
  1119.         hint.width = hint.x * 2;
  1120.         hint.height = hint.y * 3;
  1121.         help = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
  1122.                 hint.x, hint.y, hint.width, hint.height, 5, 0, 1);
  1123.         XSetWindowBackground(dpy, help, BlackPixel(dpy, screen));
  1124.         /* Title */
  1125.         XSetStandardProperties(dpy,help,"Help","Help",None,argv,argc,&hint);
  1126.         /* Try to write into a new color map */
  1127.         cmap = XCreateColormap(dpy,canvas,DefaultVisual(dpy,screen),AllocAll);
  1128.         init_color(dpy, canvas, cmap, Colors, STARTCOLOR, mincolindex, maxcolor,
  1129.                 numwheels,"splines", "Splines", 0);
  1130.         /* install new color map */
  1131.         XSetWindowColormap(dpy, canvas, cmap);
  1132.         XSetWindowColormap(dpy, help, cmap);
  1133.     }
  1134.     init_contexts();
  1135.     if (useroot)
  1136.         XSelectInput(dpy,canvas,ExposureMask);
  1137.     else {
  1138.         XSelectInput(dpy,canvas,KeyPressMask|ExposureMask|StructureNotifyMask);
  1139.         XSelectInput(dpy,help,KeyPressMask|ExposureMask);
  1140.     }
  1141.     setupmem();
  1142.     for (i=0; i!=nummaps; i++) {
  1143.         initialize();
  1144.         for (;;) {
  1145.             move();
  1146.             event_loop();
  1147.             if ((num_c > limit) && stopping)
  1148.               break;
  1149.         }
  1150.         if (oflag)
  1151.             save();
  1152.         if (spin && (!useroot) && curve && (!erase_curve))
  1153.             DemoSpin(dpy, cmap, Colors, STARTCOLOR, maxcolor, delay, 1);
  1154.         freemem();
  1155.         if (ranflag)
  1156.             ranparams();
  1157.         setupmem();
  1158.         if (xor)
  1159.             e_color = c_color;
  1160.         else
  1161.             e_color = 0;
  1162.         if (!useroot)
  1163.             init_color(dpy,canvas,cmap,Colors,STARTCOLOR,
  1164.                     mincolindex,maxcolor,numwheels,"splines","Splines",0);
  1165.     }
  1166.     XSetWindowBackgroundPixmap(dpy, canvas, pixmap);
  1167.     for (i=0; i<maxcolor; i++) {
  1168.         XFreeGC(dpy, Data_GC[0][i]);
  1169.         XFreeGC(dpy, Data_GC[1][i]);
  1170.     }
  1171.     XFreePixmap(dpy, pixmap);
  1172.     XClearWindow(dpy, canvas);
  1173.     XFlush(dpy);
  1174.     Cleanup();
  1175.     exit(0);
  1176. }
  1177.